#ifndef PW_PBL_CONCEPTS_CORE_HPP
#define PW_PBL_CONCEPTS_CORE_HPP

#include <array>
#include <concepts>
#include <type_traits>

namespace pw_pbl::inline Concepts
{
	template <typename T>
	concept ccCompleteType = requires(decltype(sizeof(T))) { true; };

	namespace _concepts
	{
		template <typename T, template <typename...> class TEMPLATE>
		inline constexpr bool ccIsTemplate = false;

		template <typename... T, template <typename...> class TEMPLATE>
		inline constexpr bool ccIsTemplate<TEMPLATE<T...>, TEMPLATE> = true;

		template <template <typename...> class TEMPLATE, typename... P>
		inline constexpr TEMPLATE<P...> getTemplateType(TEMPLATE<P...> &);
	} // namespace _concepts

	template <typename T, template <typename...> class TEMPLATE>
	concept ccIsTemplate = _concepts::ccIsTemplate<std::decay_t<T>, TEMPLATE>;

	template <typename T, template <typename...> class TEMPLATE>
	concept ccDerivedFrom = requires { _concepts::getTemplateType<TEMPLATE>(std::declval<std::decay_t<T> &>()); };

	template <typename T, template <typename...> class TEMPLATE> requires ccDerivedFrom<T, TEMPLATE>
	using ttConvertTo = decltype(_concepts::getTemplateType<TEMPLATE>(std::declval<std::decay_t<T> &>()));

	template <auto A, auto B, typename LESS, typename EQUAL, typename GREATER>
	struct _ttCondition;

	template <auto A, auto B, typename LESS, typename EQUAL, typename GREATER> requires(A <=> B < 0)
	struct _ttCondition<A, B, LESS, EQUAL, GREATER>
	{
		using type = LESS;
	};

	template <auto A, auto B, typename LESS, typename EQUAL, typename GREATER> requires(A <=> B == 0)
	struct _ttCondition<A, B, LESS, EQUAL, GREATER>
	{
		using type = EQUAL;
	};

	template <auto A, auto B, typename LESS, typename EQUAL, typename GREATER> requires(A <=> B > 0)
	struct _ttCondition<A, B, LESS, EQUAL, GREATER>
	{
		using type = GREATER;
	};

	template <typename T>
	struct _ttFirstIfOnlyOne
	{
		using type = T;
	};

	template <typename T, template <typename...> class TEMPLATE>
	struct _ttFirstIfOnlyOne<TEMPLATE<T>>
	{
		using type = T;
	};

	template <typename... T>
	struct _ttSelectAnyOne;

	template <typename T>
	struct _ttSelectAnyOne<T>
	{
		using type = typename T::type;
	};

	template <typename T1, typename... TN>
	struct _ttSelectAnyOne<T1, TN...>
	{
		using type = typename std::conditional_t<requires { (typename T1::type *){}; }, T1, _ttSelectAnyOne<TN...>>::type;
	};

	/// 三路比较类型选择
	template <auto A, auto B, typename LESS, typename EQUAL, typename GREATER>
	using ttCondition = typename _ttCondition<A, B, LESS, EQUAL, GREATER>::type;

	template <typename T>
	using ttFirstIfOnlyOne = typename _ttFirstIfOnlyOne<T>::type;

	template <typename... T>
	using ttSelectAnyOne = typename _ttSelectAnyOne<T...>::type;

	template <auto... PARMS>
	struct ttToArray;

	template <std::array DATA>
	struct ttToArray<DATA>
	{
		static constexpr auto value = DATA;
		using type = std::remove_const_t<decltype(value)>;
		using element_type = std::ranges::range_value_t<type>;
	};

	template <typename T, T ELE0, T... ELE> requires std::is_scalar_v<T>
	struct ttToArray<ELE0, ELE...>
	{
		using element_type = T;
		using type = std::array<T, 1 + sizeof...(ELE)>;
		static constexpr type value = {ELE0, ELE...};
	};

	template <auto FUNC, auto... PARMS> requires requires { FUNC(PARMS...); }
	struct ttToArray<FUNC, PARMS...>
	{
		static constexpr auto value = [] {
			auto source = FUNC(PARMS...);
			using source_type = decltype(source);
			static_assert(std::ranges::sized_range<source_type>);
			constexpr auto size = [] { return std::ranges::size(FUNC(PARMS...)); }();
			std::array<std::ranges::range_value_t<source_type>, size> out;
			if constexpr (std::ranges::random_access_range<source_type>)
				for (size_t i = 0; i < size; i++)
					out[i] = source[i];
			else
			{
				auto iter = std::ranges::begin(source);
				for (size_t i = 0; i < size; i++)
					out[i] = *iter++;
			}
			return out;
		}();
		using type = std::remove_const_t<decltype(value)>;
		using element_type = std::ranges::range_value_t<type>;
	};

	template <typename T>
	using ttPatchArray = std::conditional_t<std::is_array_v<T>, std::array<std::remove_extent_t<T>, std::extent_v<T>>, T>;

	template <typename T>
	struct _ttPatchAllArray
	{
		using type = T;
	};

	template <typename T, size_t N>
	struct _ttPatchAllArray<T[N]>
	{
		using type = std::array<typename _ttPatchAllArray<T>::type, N>;
	};

	template <typename T>
	using ttPatchAllArray = _ttPatchAllArray<T>::type;
} // namespace pw_pbl::inline Concepts

#endif // PW_PBL_CONCEPTS_CORE_HPP
